package com.hdu.service;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hdu.domain.ServiceInstance;
import com.hdu.domain.Task;
import com.hdu.result.R;
import com.hdu.util.HttpUtils;
import com.hdu.utils.MySQLExecutor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.alibaba.fastjson.JSON.toJSONString;
import static com.hdu.constant.SqlTemplate.*;
import static java.lang.String.format;
import static java.util.Optional.ofNullable;


/**
 * TaskService
 */
@Slf4j
@Component
public class TaskService {

    private final static String FUTURE = "future";

    private final RedisService redisService;
    private final MySQLExecutor mySQLExecutor;
    private final Map<String, List<ServiceInstance>> TASK_NAME_2_SERVICE_INSTANCE_MAP = new HashMap<>();

    public TaskService(RedisService redisService, MySQLExecutor mySQLExecutor) {
        this.redisService = redisService;
        this.mySQLExecutor = mySQLExecutor;
    }


    public boolean addTask(String taskName, int priority, Object taskParam, long executeTime) {
        log.info("addTask , taskName : {}, priority : {}, taskParam : {}, executeTime : {} ",
                taskName, priority, taskParam, executeTime);
        val insertSql = format(INSERT_TASK, wrap(taskName), priority, wrap(toJSONString(taskParam)), executeTime);
        R res = mySQLExecutor.insert(insertSql);
        log.info("addTask success");
        return res.isSuccess();
    }

    public boolean deleteTask(Long id) {
        Task task = getTask(id);
        return ofNullable(task)
                .map(t -> {
                    val key = String.join(":", FUTURE, String.valueOf(t.getPriority()), t.getTaskName());
                    val member = toJSONString(t);
                    return mySQLExecutor.delete(format(DELETE_TASK, id)) && redisService.zrem(key, member);
                })
                .orElse(false);
    }


    public List<Task> getTasksByTimeCondition(long from, long end) {
        val querySql = format(TIME_CONDITION_QUERY_SQL, from, end);
        return mySQLExecutor.queryList(Task.class, querySql);
    }

    private Task getTask(Long id) {
        String querySql = String.format(QUERY_SQL, id);
        return mySQLExecutor.queryOne(Task.class, querySql);
    }

    public void addServiceInstance(Map<String, ServiceInstance> taskName2serviceInstance) {
        log.info("addServiceInstance, taskName2serviceInstance : {}", taskName2serviceInstance);
        taskName2serviceInstance.forEach((taskName, serviceInstance) -> {
            TASK_NAME_2_SERVICE_INSTANCE_MAP.putIfAbsent(taskName, new ArrayList<>());
            TASK_NAME_2_SERVICE_INSTANCE_MAP.get(taskName).add(serviceInstance);
        });
    }

    public void trigger(Task task) {
        val taskName = task.getTaskName();
        val taskParam = task.getTaskParam();
        val serviceInstances = TASK_NAME_2_SERVICE_INSTANCE_MAP.get(taskName);
        if (serviceInstances == null || serviceInstances.isEmpty()) {
            return;
        }
        // 随机调度
        val serviceInstance = serviceInstances.get((int) (Math.random() * serviceInstances.size()));
        try {
            JSONObject postBody = new JSONObject();
            postBody.put("taskName", taskName);
            postBody.put("taskParam", taskParam);
            val res = HttpUtils.doPost(serviceInstance.getTaskTriggerUrl(), postBody.toJSONString());
            R r = JSON.parseObject(res, R.class);
            if (r.isFail()) {
                log.error("trigger task error , taskName : {}, taskParam : {}, triggerInstance : {}, msg : {}",
                        taskName, taskParam, serviceInstance, r.getMsg());
            }
        } catch (IOException e) {
            log.error("trigger task error , taskName : {}, taskParam : {}, triggerInstance : {}",
                    taskName, taskParam, serviceInstance, e);
        }
    }

    private String wrap(String str) {
        return "'" + str + "'";
    }
}
